﻿using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using OpenTK;
using OpenTK.Graphics;
using OpenTK.Graphics.OpenGL;
using DDD;


namespace DevApp {
    public class HelloGL3 : GameWindow {

        Graphics3D g3d;
        World wld;

        public HelloGL3 ()
            : base (640, 480,
              new GraphicsMode (), "DDD: Bouncing Balls", 0,
              DisplayDevice.Default, 3, 0,
              GraphicsContextFlags.ForwardCompatible | GraphicsContextFlags.Debug) {
        }


        protected override void OnLoad (System.EventArgs e) {

            var rnd = new System.Random (2);

            var vert = new VertexShader ("Shaders/phong.vert");
            var frag = new FragmentShader ("Shaders/phong.frag");
            var prog = new ShaderProgram (vert, frag);

            var unifArray = new UniformArray ();
            unifArray.Add ("ModelViewMatrix", UniformArray.CurrentNode, () => ((Node)null).LocalToCamera);
            unifArray.Add ("ModelViewNormalMatrix", UniformArray.CurrentNode, () => ((Node)null).LocalToCamera3x3);
            unifArray.Add ("ProjectionMatrix", UniformArray.CurrentActiveCamera, () => ((Camera)null).ProjectionMatrix);
            unifArray.Add ("Ambient", UniformArray.CurrentMaterial, () => ((PhongMaterial)null).Ambient);
            unifArray.Add ("Diffuse", UniformArray.CurrentMaterial, () => ((PhongMaterial)null).Diffuse);
            unifArray.Add ("Specular", UniformArray.CurrentMaterial, () => ((PhongMaterial)null).Specular);


            // シーン
            wld = new World ();

            var cam = new Camera ();
            var camNode = new Node ();
            cam.SetPerspective (45, 1.333f, 0.01f, 100);
            camNode.Attach (cam);
            camNode.UserID = 1;
            camNode.SetTranslation (0, 2, 10);
            wld.AddChild (camNode);
            wld.SetActiveCamera (camNode);

            // ボールx10
            for (var i=0; i < 10; i++) {
                var r = ((float)rnd.NextDouble () / 2.0f + 0.5f);
                var g = ((float)rnd.NextDouble () / 2.0f + 0.5f);
                var b = ((float)rnd.NextDouble () / 2.0f + 0.5f);

                var mat = new PhongMaterial ();
                mat.SetAmbient (0.2f, 0.2f, 0.2f);
                mat.SetDiffuse (r, g, b, 1);
                mat.SetSpecular (1, 1, 1, 100);

                var app = new Appearance ();
                app.SetShaderProgram (prog);
                app.SetMaterial (mat);
                app.AddUniformArray (unifArray);

                var mesh = MeshBuilder.CreateSphere (0.2f, 12, 12, "v_Position", "v_Normal");
                mesh.SetAppearance (app);

                var ball = new BounceBall ();

                var node = new Node ();
                node.UserID = 100 + i;
                node.Translate (-4.5f + i, (float)(rnd.NextDouble () * 2 + 3), 0);
                node.Attach (mesh);
                node.Attach (ball);

                wld.AddChild (node);
            }

            g3d = Graphics3D.GetInstance ();

            // レンダリングパスの指定
            g3d.RenderPass += delegate {
                g3d.SetWorld (wld);
                g3d.SetClearColor (0, 0, 1, 1);
                g3d.SetClearDepth (1);
                g3d.SetColorClearEnabled (true);
                g3d.SetDepthClearEnabled (true);
                g3d.SetViewort (0, 0, Width, Height);
            };

            // 未実装
            VSync = VSyncMode.On;
            GL.Enable (EnableCap.DepthTest);

        }

        protected override void OnUnload (EventArgs e) {
            wld = null;
            GC.Collect ();
            g3d.Release ();
        }


        protected override void OnUpdateFrame (FrameEventArgs e) {

            wld.Update (0);

            if (Keyboard[OpenTK.Input.Key.Escape])
                Exit ();
        }

        protected override void OnRenderFrame (FrameEventArgs e) {

            g3d.Render ();

            SwapBuffers ();
        }

        /*
        [STAThread]
        public static void Main () {
            using (HelloGL3 example = new HelloGL3 ()) {
                example.Run (30);
            }
        }
         * */
        
    }
}